//
//  CWBaseView.m
//  CWDevelop
//
//  Created by LittoCats on 7/11/14.
//  Copyright (c) 2014 Littocats. All rights reserved.
//

#import "CWBaseView.h"
#import "CWCore.h"

#import "CWViewStaticTouchEventAnalyzer.h"

#import <QuartzCore/QuartzCore.h>
#import <objc/message.h>
#import <objc/objc-exception.h>

CWAutoLayoutAlign anaylzeCWAutoLayoutAlign(NSString *alignScript){
    if (!alignScript)
        return CWAutoLayoutAlignLeft | CWAutoLayoutAlignTop;
    
    NSArray *alignScripts = [[alignScript stringByReplacingOccurrencesOfString:@" " withString:@""] componentsSeparatedByString:@"|"];
    
    CWAutoLayoutAlign align = 0;
    for (NSString *script in alignScripts) {
        align |= ([script isEqualToString:@"top"] ? CWAutoLayoutAlignTop :
                  [script isEqualToString:@"bottom"] ? CWAutoLayoutAlignBottom :
                  [script isEqualToString:@"left"] ? CWAutoLayoutAlignLeft :
                  [script isEqualToString:@"right"] ? CWAutoLayoutAlignRight :
                  [script isEqualToString:@"center"] ? CWAutoLayoutAlignTop | CWAutoLayoutAlignRight | CWAutoLayoutAlignLeft |CWAutoLayoutAlignBottom :
                  0);
    }
    align |= !(align & CWAutoLayoutAlignLeft) && !(align & CWAutoLayoutAlignRight) ? CWAutoLayoutAlignLeft : 0;
    align |= !(align & CWAutoLayoutAlignTop) && !(align & CWAutoLayoutAlignBottom) ? CWAutoLayoutAlignTop : 0;
    return align;
}

CWRim CWRimMake(CGFloat top, CGFloat bottom, CGFloat left, CGFloat right){
    CWRim rim;
    rim.top = top;rim.bottom = bottom;rim.left = left;rim.right = right;
    return rim;
}

static CWRim CWRimWithScript(id script);
static CWRim CWRimWithScript(id script){
    if (!script) return CWRimMake(0,0,0,0);
    
    CWRim rim;
    if ([script isKindOfClass:[NSDictionary class]]){
        rim.top = [script[@"top"] floatValue];
        rim.bottom = [script[@"bottom"] floatValue];
        rim.left = [script[@"left"] floatValue];
        rim.right = [script[@"right"] floatValue];
    }else if ([script isKindOfClass:[NSNumber class]])
        rim.top = rim.bottom = rim.left = rim.right = [script floatValue];
    return rim;
}

static NSDictionary *CWAutoLayoutViewMap();
static NSDictionary *CWAutoLayoutViewMap()
{
    static NSDictionary *viewMap = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSString *mapSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"CWAutoLayoutViewMap"
                                                                                                 ofType:@"json"]
                                                        encoding:NSUTF8StringEncoding error:nil];
        mapSource = [mapSource stringByReplacingOccurrencesOfString:@"/\\*([\\u0000-\\uFFFF]+?)\\*/|//[^\\n]+"
                                                         withString:@""
                                                            options:NSRegularExpressionSearch
                                                              range:NSMakeRange(0, mapSource.length)];
        viewMap = [NSJSONSerialization JSONObjectWithData:[mapSource dataUsingEncoding:NSUTF8StringEncoding]
                                                  options:0
                                                    error:nil];
    });
    return viewMap;
}

static NSDictionary *CWAutoLayoutViewDefaultAttributes();
static NSDictionary *CWAutoLayoutViewDefaultAttributes()
{
    static NSDictionary *attributesMap = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSString *mapSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"CWAutoLayoutViewDefaultAttributes"
                                                                                                 ofType:@"json"]
                                                        encoding:NSUTF8StringEncoding
                                                           error:nil];
        mapSource = [mapSource stringByReplacingOccurrencesOfString:@"/\\*([\\u0000-\\uFFFF]+?)\\*/|//[^\\n]+"
                                                         withString:@""
                                                            options:NSRegularExpressionSearch
                                                              range:NSMakeRange(0, mapSource.length)];
        attributesMap = [NSJSONSerialization JSONObjectWithData:[mapSource dataUsingEncoding:NSUTF8StringEncoding]
                                                        options:NSJSONReadingMutableContainers
                                                          error:nil];
    });
    return attributesMap;
}

static NSArray *generalAttributeSuportList();
static NSArray *generalAttributeSuportList()
{
    static NSArray *attributesMap = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSString *mapSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"CWViewGeneralAttributesSuportList"
                                                                                                 ofType:@"json"]
                                                        encoding:NSUTF8StringEncoding
                                                           error:nil];
        mapSource = [mapSource stringByReplacingOccurrencesOfString:@"/\\*([\\u0000-\\uFFFF]+?)\\*/|//[^\\n]+"
                                                         withString:@""
                                                            options:NSRegularExpressionSearch
                                                              range:NSMakeRange(0, mapSource.length)];
        attributesMap = [NSJSONSerialization JSONObjectWithData:[mapSource dataUsingEncoding:NSUTF8StringEncoding]
                                                        options:NSJSONReadingMutableContainers
                                                          error:nil];
    });
    return attributesMap;
}

@implementation  UIView (CWCustomAttributes)

@dynamic attributes;
static const char *kCWUIViewAttributesKey;
- (NSMutableDictionary *)attributes
{
    NSMutableDictionary *attributes = objc_getAssociatedObject(self, &kCWUIViewAttributesKey);
    if (!attributes) {
        attributes = [NSMutableDictionary new];
        objc_setAssociatedObject(self, &kCWUIViewAttributesKey, attributes, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return attributes;
}

@dynamic margin;
- (CWMargin)margin
{
    return CWRimWithScript(self.attributes[@"margin"]);
}
- (void)setMargin:(CWMargin)margin
{
    if (!self.attributes[@"margin"])
        [self.attributes setObject:[NSMutableDictionary new] forKey:@"margin"];
    
        [self.attributes[@"margin"] addEntriesFromDictionary:@{@"top": @(margin.top),
                                                               @"bottom":@(margin.bottom),
                                                               @"left": @(margin.left),
                                                               @"right": @(margin.right)}];
}

@dynamic padding;
- (CWPadding)padding
{
    return CWRimWithScript(self.attributes[@"padding"]);
}
- (void)setPadding:(CWPadding)padding
{
    if (!self.attributes[@"padding"])
        [self.attributes setObject:[NSMutableDictionary new] forKey:@"padding"];
    [self.attributes[@"padding"] addEntriesFromDictionary:@{@"top": @(padding.top),
                                                           @"bottom":@(padding.bottom),
                                                           @"left": @(padding.left),
                                                           @"right": @(padding.right)}];
}

@dynamic align;
- (CWAutoLayoutAlign)align
{
    CWAutoLayoutAlign align = [self.attributes[@"align"] integerValue];
    if (!(align & (CWAutoLayoutAlignTop | CWAutoLayoutAlignBottom | CWAutoLayoutAlignLeft | CWAutoLayoutAlignRight))) {
        align = anaylzeCWAutoLayoutAlign(self.attributes[@"align"]);
        [self.attributes setObject:@(align) forKey:@"align"];
    }
    return align;
}

@dynamic size;
- (CGSize)size
{
    return self.frame.size;
}
- (void)setSize:(CGSize)size
{
    CGRect frame = self.frame;
    frame.size = size;
    [self setFrame:frame];
    [self.attributes setObject:@{@"w":@(size.width),@"h":@(size.height)} forKey:@"size"];
    [self.attributes setObject:@(size.width) forKey:@"width"];
    [self.attributes setObject:@(size.height) forKey:@"height"];
}

@end

@implementation UIView (CWUIJSONView)

+ (instancetype)instanceWithJSONLayout:(NSDictionary *)JSONLayout
{
    //解析 viewType
    NSString *viewType = JSONLayout[@"type"];
    Class viewClass = NSClassFromString(viewType);
    if (!viewClass || ![viewClass isSubclassOfClass:[UIView class]]) {
        viewType = CWAutoLayoutViewMap()[viewType];
        viewClass = NSClassFromString(viewType);
        if (!viewClass || ![viewClass isSubclassOfClass:[UIView class]]){
            CWLog(@"[CWInterface] warning : Class of type< %@ > is not exist, will use CWBaseView .",viewType);
            viewType = @"CWBaseView";
            viewClass = [CWBaseView class];
        }
    }
    UIView *instance = [viewClass alloc];
    
    //解析jsonLayout
    [instance.attributes addEntriesFromDictionary:JSONLayout];
    [instance.attributes removeObjectForKey:@"subviews"];
    [CWAutoLayoutViewDefaultAttributes()[viewType] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        if ([instance.attributes[key] isEmpty]) {
            [instance.attributes setObject:obj forKey:key];
        }
    }];
    
    instance = [instance init];
    
    for (NSMutableDictionary *subJSONLayout in JSONLayout[@"subviews"]) {
        if (instance.attributes[@"___context"]) {
            [subJSONLayout setObject:instance.attributes[@"___context"] forKey:@"___context"];
        }
        [instance addSubview:[UIView instanceWithJSONLayout:subJSONLayout]];
    }
    if (instance.attributes[@"___context"] && instance.attributes[@"id"])
        [[NSObject findInstanceWithId:instance.attributes[@"___context"]] evaluateScript:[NSString stringWithFormat:@"var %@ = \"%@\";",
                                                                                          instance.attributes[@"id"],
                                                                                         instance._id]];
    return instance;
}

#pragma mark- override init
- (id)init
{
    if (!self.layer) {
        if ([self isMemberOfClass:[UIView class]])
            self = [self initWithFrame:CGRectZero];
        else{
            struct objc_super superIns = {
                self,
                [self superclass]
            };
            self = objc_msgSendSuper(&superIns, @selector(initWithFrame:), CGRectZero);
        }
    }

    if ([objc_getAssociatedObject(self, &kCWUIViewAttributesKey) count])
        [self __defaultInit];
    
    return self;
}

#pragma mark- set properties default from attribute <JSONLayout>
- (void)__defaultInit
{
    [CWViewGeneralAttributesSetter setGeneralAttributes:self.attributes ofCWView:self];
    [CWViewStaticTouchEventAnalyzer anaylzeTouchEvents:self.attributes[@"touchEvents"] ofView:self];
}

#pragma mark- onClickEvent
- (void)setOnClickEvent:(NSDictionary *)onClickEvent
{
    if (!onClickEvent) return;
    [[CWViewStaticTouchEventAnalyzer defaultAnalyzer] addTouchResponder:onClickEvent toView:self ofType:@"onClick"];
}

#pragma mark- analyze resource
- (NSString *)analyzePath:(NSString *)path
{
    return [[NSObject findInstanceWithId:self.attributes[@"___context"]] analyzeResourcePath:path];
}
@end

@implementation CWViewGeneralAttributesSetter

+ (void)setGeneralAttributes:(NSDictionary *)attributesMap ofCWView:(id)view
{
    for (NSString *key in [attributesMap allKeys]) {
        if ([generalAttributeSuportList() containsObject:key])
            objc_msgSend(self, NSSelectorFromString([NSString stringWithFormat:@"instance:%@:",key]), view, attributesMap[key]);
        else if ([attributesMap[key] isKindOfClass:[NSDictionary class]] && [key isEqualToString:@"border"])
            [self setGeneralAttributes:attributesMap[key] ofCWView:view];
    }
}

CWViewGeneralAttributeSetter(backgroundColor,instance, @selector(setBackgroundColor:),CWColorWithScript(params))

CWViewGeneralAttributeSetter(alpha, instance, @selector(setAlpha:), [params floatValue])

CWViewGeneralAttributeSetter(enable, instance, @selector(setUserInteractionEnabled:), [params boolValue])

CWViewGeneralAttributeSetter(hidden, instance, @selector(setHidden:), [params boolValue])

CWViewGeneralAttributeSetter(frame, instance, @selector(setFrame:),
                             CGRectMake([params[@"x"] floatValue],
                                        [params[@"y"] floatValue],
                                        [params[@"w"] floatValue],
                                        [params[@"h"] floatValue]))

CWViewGeneralAttributeSetter(size, instance, @selector(setSize:), CGSizeMake([params[@"w"] floatValue],
                                                                             [params[@"h"] floatValue]))

CWViewGeneralAttributeSetter(center, instance, @selector(setCenter:), CGPointMake([params[@"x"] floatValue],
                                                                                  [params[@"y"] floatValue]))

#pragma mark- layer style
CWViewGeneralAttributeSetter(borderColor, [instance layer], @selector(setBorderColor:), [[UIColor colorWithScript:params] CGColor])
//CWViewGeneralAttributeSetter(borderWidth, [instance layer], @selector(setBorderWidth:), [params floatValue])
//CWViewGeneralAttributeSetter(cornerRadius, [instance layer], @selector(setCornerRadius:), [params floatValue])

//在arm上 objc_msgSend 参数为float时，发生异常，设置属性不成功
+(void)instance:(id)instance borderWidth:(id)params
{
    if ([[instance layer] respondsToSelector:@selector(setBorderWidth:)])
        [[instance layer] setBorderWidth:[params floatValue]];
}
+(void)instance:(id)instance cornerRadius:(id)params
{
    if ([[instance layer] respondsToSelector:@selector(setCornerRadius:)])
        [[instance layer] setCornerRadius:[params floatValue]];
}

#pragma mark- button label textField textView
CWViewGeneralAttributeSetter(text, instance, @selector(setText:), params);

CWViewGeneralAttributeSetter(textAlignment, instance, @selector(setTextAlignment:),
                             [params UTF8String] == "right" ? NSTextAlignmentRight : [params UTF8String] == "center" ? NSTextAlignmentCenter : NSTextAlignmentLeft)

CWViewGeneralAttributeSetter(textColor, instance, @selector(setTextColor:), CWColorWithScript(params))

CWViewGeneralAttributeSetter(font, instance, @selector(setFont:),[UIFont fontWithScript:params])

#pragma mark- imageview
CWViewGeneralAttributeSetter(imageSource, instance,NSSelectorFromString(@"setImageSource:"), [instance analyzePath:params])

@end

@implementation CWViewGeneralAttributesGetter

+ (id)getGeneralAttributes:(id)attribute ofCWView:(id)view
{
    id result;
    if ([attribute isKindOfClass:[NSArray class]]){
        result = [NSMutableDictionary new];
        [attribute enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            if ([generalAttributeSuportList() containsObject:obj])
                [result setObject:objc_msgSend(self,
                                              NSSelectorFromString([NSString stringWithFormat:@"%@:ofInstance:",obj]),
                                              nil, view)
                           forKey:obj];
        }];
    }else if ([attribute isKindOfClass:[NSString class]]){
        if ([generalAttributeSuportList() containsObject:attribute])
            result = objc_msgSend(self,
                                  NSSelectorFromString([NSString stringWithFormat:@"%@:ofInstance:",attribute]),
                                  nil, view);
    }
    return result;
}

+ (NSDictionary *)kCWDictionaryFromCGRect:(CGRect)rect
{
    return @{@"x":@(rect.origin.x),
             @"y":@(rect.origin.y),
             @"w":@(rect.size.width),
             @"h":@(rect.size.height)};
}

+ (NSDictionary *)kCWDictionaryFromCGPoint:(CGPoint)point
{
    return @{@"x": @(point.x),
             @"y": @(point.y)};
}
CWViewGeneralAttributeGetter(backgroundColor, @selector(backgroundColor), [CWViewGeneralAttributeGetterResult_id script])

CWViewGeneralAttributeGetter(alpha, @selector(alpha), @([instance alpha]))

CWViewGeneralAttributeGetter(enable, @selector(isUserInteractionEnabled), @(CWViewGeneralAttributeGetterResult_float == YES))

CWViewGeneralAttributeGetter(hidden, @selector(isHidden), @(CWViewGeneralAttributeGetterResult_float == YES))

CWViewGeneralAttributeGetter(frame, @selector(frame), [self kCWDictionaryFromCGRect:[instance frame]])

CWViewGeneralAttributeGetter(size, @selector(size), [(UIView *)instance attributes][@"size"])

CWViewGeneralAttributeGetter(center, @selector(center), [self kCWDictionaryFromCGPoint:[instance center]])

CWViewGeneralAttributeGetter(borderColor, @selector(layer), [[UIColor colorWithCGColor:[instance layer].borderColor] script])

CWViewGeneralAttributeGetter(borderWidth, @selector(layer), @([instance layer].borderWidth))

CWViewGeneralAttributeGetter(cornerRadius, @selector(layer), @([instance layer].cornerRadius))


#pragma mark- button label textField textView
CWViewGeneralAttributeGetter(text, @selector(text), [instance text])

CWViewGeneralAttributeGetter(textAlignment, @selector(textAlignment), [instance textAlignment] == NSTextAlignmentLeft ? @"left" :
                                                                      [instance textAlignment] == NSTextAlignmentRight ? @"right" :
                                                                      [instance textAlignment] == NSTextAlignmentCenter ? @"center" : @"")

CWViewGeneralAttributeGetter(textColor, @selector(textColor), [[instance textColor] script])

CWViewGeneralAttributeGetter(font, @selector(font), [[instance font] script])

@end

@implementation UIView (CWUIViewOverlay)

@dynamic overlays;
static const char *kCWUIViewOverlaysKey;
- (NSMutableArray *)overlays
{
    NSMutableArray *overlays = objc_getAssociatedObject(self, &kCWUIViewOverlaysKey);
    if (!overlays) {
        overlays = [NSMutableArray new];
        objc_setAssociatedObject(self, &kCWUIViewOverlaysKey, overlays, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return overlays;
}

- (void)addOverlay:(UIView *)overlay
{
    [self addSubview:overlay];
}
@end

@implementation CWBaseView

@end
